/****************************************************
	文件：CameraRenderer.cs
	作者：HuskyT
	邮箱：1005240602@qq.com
	日期：2020/9/9 11:45:33
	功能：处理单个摄像机的渲染
*****************************************************/

using UnityEngine;
using UnityEngine.Rendering;

namespace LearningSRP
{
    public partial class CameraRenderer
    {
        /// <summary>
        /// 渲染上下文
        /// </summary>
        private ScriptableRenderContext mContext;
        /// <summary>
        /// 当前相机
        /// </summary>
        private Camera mCamera;
        /// <summary>
        /// 命令缓冲 名称
        /// </summary>
        private const string mBufferName = "Render Camera";
        /// <summary>
        /// 命令缓冲
        /// </summary>
        private CommandBuffer mBuffer = new CommandBuffer { name = mBufferName };
        /// <summary>
        /// 裁剪结果
        /// </summary>
        private CullingResults mCullingResults;
        /// <summary>
        /// 无光 Shader 的 Tag Id
        /// </summary>
        private static ShaderTagId mUnlitShaderTagId = new ShaderTagId("SRPDefaultUnlit");
        private static ShaderTagId mLitShaderTagId = new ShaderTagId("CustomLit");

        /// <summary>
        /// 直接光
        /// </summary>
        private Lighting mLighting = new Lighting();

        /// <summary>
        /// 执行当前单个相机的渲染
        /// </summary>
        /// <param name="context">渲染上下文</param>
        /// <param name="camera">当前相机</param>
        /// <param name="useDynamicBatching">是否使用动态批处理</param>
        /// <param name="useGPUInstancing">是否使用GPU实例</param>
        public void Render(ScriptableRenderContext context, Camera camera, bool useDynamicBatching, bool useGPUInstancing)
        {
            mContext = context;
            mCamera = camera;
#if UNITY_EDITOR
            PrepareBuffer();
            PrepareForSceneWindow();
#endif
            if (Cull() == false) return;
            Setup();
            mLighting.Setup(context, mCullingResults); //设置 直接光数据
            DrawVisibleGeometry(useDynamicBatching, useGPUInstancing);
#if UNITY_EDITOR
            DrawUnsupportedShaders();
            DrawGizmos();
#endif
            Submit();
        }

        /// <summary>
        /// 渲染前的相关设置
        /// </summary>
        private void Setup()
        {
            //设置相机相关属性（VP矩阵）
            mContext.SetupCameraProperties(mCamera);
            //每进行一次渲染循环前都需要清空一下当前的缓冲（深度缓冲、颜色缓冲、模板缓冲）
            CameraClearFlags flags = mCamera.clearFlags; //当前相机的 ClearFlag
            mBuffer.ClearRenderTarget(
                flags <= CameraClearFlags.Depth,
                flags == CameraClearFlags.Color,
                flags == CameraClearFlags.Color ? mCamera.backgroundColor.linear : Color.clear);
            //开启采样：使得 Command 能内嵌入 FrameDebugger 中，便于调试时识别
            mBuffer.BeginSample(mSampleName);
            ExecuteBuffer();
        }

        /// <summary>
        /// 绘制可见图形，将绘制 Command 提交到 Context 中的缓冲 work 队列
        /// </summary>
        /// <param name="useDynamicBatching">是否使用动态批处理</param>
        /// <param name="useGPUInstancing">是否使用GPU实例</param>
        private void DrawVisibleGeometry(bool useDynamicBatching, bool useGPUInstancing)
        {
            //先渲染 Opaque => SkyBox => Transparent

            //1.渲染 Opaque：
            SortingSettings sortingSettings = new SortingSettings(mCamera)
            {
                criteria = SortingCriteria.CommonOpaque, //设定渲染顺序
            };
            DrawingSettings drawingSettings = new DrawingSettings(mUnlitShaderTagId, sortingSettings)
            {
                enableDynamicBatching = useDynamicBatching,
                enableInstancing = useGPUInstancing,
            }; //渲染 Unlit Shader
            drawingSettings.SetShaderPassName(1, mLitShaderTagId); //渲染 Lit Shader
            FilteringSettings filteringSettings = new FilteringSettings(RenderQueueRange.opaque); //渲染不透明物体
            //绘制 Object
            mContext.DrawRenderers(mCullingResults, ref drawingSettings, ref filteringSettings);
            //2.绘制天空盒：将绘制天空盒的 Command 提交到缓冲的 work 队列中
            mContext.DrawSkybox(mCamera);
            //3.渲染 Transparent：
            //透明物体需要 back-to-front 渲染，以获得正确的 blend 效果
            sortingSettings.criteria = SortingCriteria.CommonTransparent;
            drawingSettings.sortingSettings = sortingSettings;
            filteringSettings.renderQueueRange = RenderQueueRange.transparent;
            mContext.DrawRenderers(mCullingResults, ref drawingSettings, ref filteringSettings);
        }

        /// <summary>
        /// 提交、执行 context 中缓冲的 work 队列中的 Command
        /// </summary>
        private void Submit()
        {
            mBuffer.EndSample(mSampleName);
            ExecuteBuffer();
            mContext.Submit();
        }

        /// <summary>
        /// 执行命令缓冲
        /// </summary>
        private void ExecuteBuffer()
        {
            mContext.ExecuteCommandBuffer(mBuffer);
            mBuffer.Clear();
        }

        /// <summary>
        /// 裁剪
        /// </summary>
        /// <returns></returns>
        private bool Cull()
        {
            ScriptableCullingParameters parameters;
            if (mCamera.TryGetCullingParameters(out parameters))
            {
                mCullingResults = mContext.Cull(ref parameters); //裁剪
                return true;
            }
            return false;
        }

    }
}
